#include "Socket.h"
#include "../Utils/Utils.h"
#include "../Utils/DebugLog.h"

#ifdef WIN32
#include <process.h>
#include <windows.h>
#endif

#include <iostream>




bool	Socket::m_isInitWinSock	= false; 
int		Socket::m_socketCounter	= 0; 

//====================================================================
//
//
//====================================================================
Socket::Socket(int type, const char* ip, unsigned short port)
{
	socketType = type;
	m_socketCounter++;

	// Init winsock for windows
	#ifdef WIN32
		if(!m_isInitWinSock)
		{
			m_isInitWinSock = true;
			WORD winsock_version = MAKEWORD(2,0);		// request winsock v2.0
			WSADATA winsock_data;						

			int error = WSAStartup(winsock_version, &winsock_data);	// initializing
			if(error != 0)
				DebugLog::getInstance()->log("Socket::Socket - could not initialize winsock\n");
		}
	#endif

	address = new SocketAddress(ip, port);

	m_socket = socket(PF_INET, type, 0);			// create socket
	if(m_socket == SOCKET_ERROR)
	{
		DebugLog::getInstance()->log("Socket::Socket - could not create a socket\n");
	}

	// bind
	int error = bind(m_socket, (struct sockaddr*)&address->getAddr(), sizeof(struct sockaddr_in));
	if(error == SOCKET_ERROR)
	{
		DebugLog::getInstance()->log("Socket::Socket - could not bind socket to a provided name\n");
	}

	m_data = new ByteArray();
}

//====================================================================
//
//
//====================================================================
#ifdef WIN32
Socket::Socket(SOCKET sock, sockaddr_in addr)
#else
Socket::Socket(int sock, sockaddr_in addr)
#endif
{
	socketType = SOCKET_TCP;
	m_socketCounter++;

	// Init winsock for windows
	#ifdef WIN32
		if(!m_isInitWinSock)
		{
			m_isInitWinSock = true;
			WORD winsock_version = MAKEWORD(2,0);		// request winsock v2.0
			WSADATA winsock_data;						

			int error = WSAStartup(winsock_version, &winsock_data);	// initializing
			if(error != 0)
				DebugLog::getInstance()->log("Socket::Socket - could not initialize winsock\n");
		}
	#endif

	m_socket = sock;
	address = new SocketAddress(addr);

	m_data = new ByteArray();
}

//====================================================================
//
//
//====================================================================
Socket::~Socket()
{
	Disconnect();	

	if(address != NULL)
	{
		delete address;
		address = NULL;
	}

	if(m_data != NULL)
	{
		delete m_data;
		m_data = NULL;
	}
	
	if(--m_socketCounter <= 0)
	{
		m_socketCounter = 0;
		// deinitialize winsock
		#ifdef WIN32
			if(m_isInitWinSock)
			{
				m_isInitWinSock = false;

				int error = WSACleanup();					
				if(error == SOCKET_ERROR)
				{
					DebugLog::getInstance()->log("Socket::~Socket - could not perform cleaning up\n");
				}
			}
		#endif
	}
}

//====================================================================
//
//
//====================================================================
void Socket::Disconnect()
{	
	if(isAlive())
	{
		shutdown(m_socket, SD_BOTH);
		closesocket(m_socket);
		m_socket = INVALID_SOCKET;
	}
}

//====================================================================
//
//
//====================================================================
void Socket::Listen(int maxConnections)
{
	if(socketType == SOCKET_TCP)
	{
		listen(m_socket, maxConnections);
	}
}

//====================================================================
//
//
//====================================================================
Socket* Socket::Accept()
{
	if(socketType == SOCKET_TCP)
	{
		SOCKET client = 0;
		struct sockaddr_in client_addr;
		int client_addrlen = sizeof(struct sockaddr);

		client = accept(m_socket, (sockaddr*)&client_addr, &client_addrlen);
		return new Socket(client, client_addr);		
	}

	return NULL;
}

//====================================================================
//
//
//====================================================================
bool Socket::Select(int mode)
{
	fd_set set;

	FD_ZERO(&set);
	FD_SET(m_socket, &set);
	int h;
	timeval tval = {0, 0};
	if(mode == SELECT_READ)
	{
		h = select(m_socket + 1, &set, NULL, NULL, &tval);
	}
	else
	{
		h = select(m_socket + 1, NULL, &set, NULL, &tval);
	}

	if(h > 0 && m_socket != INVALID_SOCKET && FD_ISSET(m_socket, &set))
	{
		return true;
	}
	else if(h < 0)
	{
		Disconnect();
		return false;
	}
	
	return false;
}

//====================================================================
//
//
//====================================================================
bool Socket::NonBlock()
{
	unsigned long x = 1;
	int err = ioctlsocket(m_socket, FIONBIO, (unsigned long*)&x);
	if(err == SOCKET_ERROR)
	{
		return false;
	}

	return true;
}

//====================================================================
//
//
//====================================================================
void Socket::sendByteArray(ByteArray* data)
{
	if(!isAlive())
	{
		return;
	}

	int len = data->length();//Utils::swapBytes(data->length());		// to big-endian
	memcpy(buff, (char*)&len, sizeof(int));								// len
	memcpy(buff + sizeof(int), data->readBytes(), len);	// data
	Send(buff, len + sizeof(int));
}

//====================================================================
//
//
//====================================================================
int Socket::recvByteArray()
{
	if(!isAlive())
	{
		return 0;
	}

	unsigned int numBytesRecv	= 0;
	unsigned int numBytesMsg	= 0;
	
	m_data->clear();

	// Read byte array length
	numBytesRecv = recv(m_socket, buff, sizeof(int), 0); 
	if (m_socket == INVALID_SOCKET || numBytesRecv < sizeof(int))
	{
		Disconnect();
		return 0;
	}
	memcpy(&numBytesMsg, buff, sizeof(int)); //(buff[0] << 24) | (buff[1] << 16) | (buff[2] << 8) | (buff[3]);
	
	numBytesRecv = recv(m_socket, buff, sizeof(buff), 0);
	if (m_socket == INVALID_SOCKET || numBytesRecv <= 0)
	{
		DebugLog::getInstance()->log("int Socket::recvByteArray()\n");
		Disconnect();
		return 0;
	}
	m_data->writeBytes(buff, numBytesMsg);

	return numBytesMsg;
}

//====================================================================
//
//
//====================================================================
int Socket::Recv(char *data)
{
	if(!isAlive())
	{
		return 0;
	}

	unsigned int numBytesRecv	= 0;
	unsigned int numBytesMsg	= 0;

	//while((numBytesRecv = recv(m_socket, buff, sizeof(buff), 0)) > 0)
	{
		numBytesRecv = recv(m_socket, buff, sizeof(buff), 0);
		memcpy(data + numBytesMsg, buff, numBytesRecv);
		numBytesMsg += numBytesRecv;
		if (numBytesRecv < 0)
		{
			DebugLog::getInstance()->log("int Socket::Recv(char *data)\n");
			Disconnect();
			return 0;
		}
	}

	return numBytesRecv;
}

//====================================================================
//
//
//====================================================================
void Socket::Send(char *data, int len)
{
	if(!isAlive())
	{
		return;
	}

	int error = send(m_socket, data, len, 0);
	if(error == SOCKET_ERROR)
	{
		Disconnect();
	}
}

//====================================================================
//
//
//====================================================================
ByteArray* Socket::getBuffer()
{
	return m_data;
}

//====================================================================
//
//
//====================================================================
bool Socket::isAlive()
{
	if(m_socket != INVALID_SOCKET && m_socket != NULL)
	{
		return true;
	}

	return false;
}
